home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / MacCalendar 1.0d5 / Src / DrawCalendar.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-30  |  8.8 KB  |  288 lines  |  [TEXT/KAHL]

  1. /*                                    DrawCalendar.c                                    */
  2. /*
  3.  * DrawCalendar.c
  4.  * Copyright © 1994 Apple Computer Inc. All rights reserved.
  5.  * DrawCalendar displays the calendar (Gregorian) for the selected date. The
  6.  * algorithm has been simplified and consequently will only work for dates within
  7.  * the Macintosh epoch.
  8.  *
  9.  * Note that DrawCalendar is common to the Control Strip and Setup application.
  10.  * If you change it, you must rebuild both modules.
  11.  */
  12. #include <Fonts.h>
  13. #include <IntlResources.h>
  14. #include <Memory.h>
  15. #include <OSUtils.h>
  16. #include <Packages.h>
  17. #include <QuickDraw.h>
  18. #include <Script.h>
  19. #include <TextUtils.h>
  20. #include "MacCalendar.h"
  21. #define    kFebruary        2                        /* The magic month                    */
  22. #define width(rect)        ((rect).right - (rect).left)
  23. #define height(rect)    ((rect).bottom - (rect).top)
  24.  
  25. /*
  26.  * This character vector contains the number of days in a month. Because compilers
  27.  * do not necessarily allow pc-relative addressing of generic vectors, we define
  28.  * it as a character string.
  29.  *  034        28
  30.  *    035        29
  31.  *    036        30
  32.  *    037        31
  33.  */                    /*    Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec            */
  34. #define kDayInMonth "\000\037\034\037\036\037\036\037\037\036\037\036\037"
  35.  
  36. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  37.  * GetCalendarDisplaySize
  38.  *
  39.  * Return the width and height of a rectangle needed to display the calendar in the
  40.  * specified font and font size. This is needed to draw the calendar and to position
  41.  * the calendar display with respect to the Control Strip.
  42.  */
  43. Point
  44. GetCalendarDisplaySize(
  45.         short                    fontNumber,        /* Display font                        */
  46.         short                    fontSize        /* Display font size                */
  47.     )
  48. {
  49.         FontInfo                fontInfo;
  50.         short                    dateWidth;
  51.         short                    lineHeight;
  52.         Point                    result;
  53.         short                    saveTextFont;
  54.         short                    saveTextSize;
  55.         short                    saveTextFace;
  56.         GrafPtr                    currentPort;
  57.         
  58.         GetPort(¤tPort);
  59.         saveTextFont = currentPort->txFont;
  60.         saveTextSize = currentPort->txSize;
  61.         saveTextFace = currentPort->txFace;
  62.         TextFont(fontNumber);
  63.         TextSize(fontSize);
  64.         TextFace(normal);
  65.         GetFontInfo(&fontInfo);
  66.         lineHeight = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
  67.         dateWidth = StringWidth("\p 00");
  68.         result.h = (dateWidth * 7) + 4;        /* 7 == days in the week                */
  69.         result.v = (lineHeight * 8) + 4;    /* 8 == lines of text in the calendar    */
  70.         TextFont(saveTextFont);
  71.         TextSize(saveTextSize);
  72.         TextFace(saveTextFace);
  73.         return (result);
  74. }
  75.  
  76. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  77.  * GetCalendarMonthRect
  78.  *
  79.  * Given a display rectangle, font, and font size, create the actual display area.
  80.  */
  81. void
  82. GetCalendarMonthRect(
  83.         short                    fontNumber,
  84.         short                    fontSize,
  85.         const Rect                *displayRect,
  86.         Rect                    *monthRect
  87.     )
  88. {
  89.         Point                    monthSize;
  90.         
  91.         monthSize = GetCalendarDisplaySize(fontNumber, fontSize);
  92.         /*
  93.          * Center the month rectangle within the drawing rectangle.
  94.          * >> 1 is used to divide by two without loading a library routine.
  95.          */
  96.         monthRect->left = displayRect->left
  97.                     + ((width(*displayRect) - monthSize.h) >> 1);
  98.         monthRect->top = displayRect->top
  99.                     + ((height(*displayRect) - monthSize.v) >> 1);
  100.         monthRect->right = monthRect->left + monthSize.h;
  101.         monthRect->bottom = monthRect->top + monthSize.v;
  102. }
  103.  
  104. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  105.  * DrawCalendar
  106.  *
  107.  * Draw the month - the port is set to the drawing port. Text font, size, and style
  108.  * are not preserved. dayName is a Pascal string with the following format, repeated
  109.  * seven times, once for each day:
  110.  *    { nByte, Byte1, Byte2, etc }. For example, if dates are represented
  111.  * by "S M Tu W Th F S", dayName would be specified in a pascal string as
  112.  *        "\p\001S\001M\002Tu\001W\002Th\001F\001S\000"
  113.  * Note: the day names must correspond to the firstDayOfWeek parameter. I.e. if the
  114.  * first day is Monday, the first word in the string is "M".
  115.  */
  116. void
  117. DrawCalendar(
  118.         short                    year,                    /* 1904 ..                    */
  119.         short                    month,                    /* January == 1                */
  120.         short                    firstDayOfWeek,            /* Sunday = 1, Monday 2        */
  121.         ConstStr255Param        dayName,                /* Day of week string        */
  122.         const Rect                *displayRect,            /* Where to draw the text    */
  123.         short                    fontNumber,                /* Display font                */
  124.         short                    fontSize                /* Display font size        */
  125.     )
  126. {
  127.         DateTimeRec                now;
  128.         unsigned long            nowSeconds;
  129.         short                    weekday;
  130.         short                    daysInMonth;
  131.         short                    today;
  132.         short                    lineHeight;
  133.         short                    dateWidth;
  134.         short                    spaceWidth;
  135.         short                    digitWidth;
  136.         FontInfo                fontInfo;
  137.         Rect                    monthRect;
  138.         short                    hPos;
  139.         short                    vPos;
  140.         register unsigned char    *dayNamePtr;
  141.         short                    dayWidth;
  142.         Intl1Hndl                intlHdl;
  143.         Str255                    work;
  144.         Boolean                    isThisMonth;
  145.         short                    thisDate;
  146.         Rect                    dayRect;
  147.         PenState                penState;
  148.         short                    hOffset;
  149.         short                    vOffset;
  150.         short                    penSize;
  151.         
  152.         /*
  153.          * Get the drawing parameters for this month. This duplicates the logic of
  154.          * GetCalendarDisplaySize above.
  155.          */
  156.         GetCalendarMonthRect(fontNumber, fontSize, displayRect, &monthRect);
  157.         TextFont(fontNumber);
  158.         TextSize(fontSize);
  159.         TextFace(normal);
  160.         GetFontInfo(&fontInfo);
  161.         lineHeight = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
  162.         spaceWidth = CharWidth(' ');
  163.         digitWidth = CharWidth('0');
  164.         dateWidth = spaceWidth + (digitWidth * 2);
  165.         /*
  166.          * If we're displaying the current month, we want to hilite today's date.
  167.          * 1.0d3
  168.          */
  169.         GetDateTime(&nowSeconds);
  170.         Secs2Date(nowSeconds, &now);
  171.         isThisMonth = (year == now.year && month == now.month);
  172.         thisDate = now.day;
  173.         /*
  174.          * Get the parameters for this particular month. We convert day 1 to seconds,
  175.          * then back to the date in order to locate the weekday corresponding to the
  176.          * first day of the month.
  177.          */
  178.         now.year = year;
  179.         now.month = month;
  180.         now.day = 1;
  181.         now.hour = 0;
  182.         now.minute = 0;
  183.         now.second = 0;
  184.         Date2Secs(&now, &nowSeconds);
  185.         Secs2Date(nowSeconds, &now);
  186.         vPos = monthRect.top + 2 + fontInfo.ascent;
  187.         /*
  188.          * Draw the month and year names.
  189.          */
  190.         intlHdl = (Intl1Hndl) IUGetIntl(1);
  191.         if (intlHdl != NULL) {
  192.             /*
  193.              * 1.0d4: Don't modify the actual data.
  194.              */
  195.             if (HandToHand((Handle *) &intlHdl) == noErr) {
  196.                 /*
  197.                  * Convert the date to "month, year" (myd + supress day)
  198.                  */
  199.                 (**intlHdl).suppressDay = 3;
  200.                 IUDatePString(nowSeconds, myd, work, (Handle) intlHdl);
  201.                 hPos = monthRect.left + 2
  202.                     + ((width(monthRect) - StringWidth(work)) >> 1);
  203.                 MoveTo(hPos, vPos);
  204.                 DrawString(work);
  205.                 vPos += lineHeight;
  206.             }
  207.             DisposeHandle((Handle) intlHdl);
  208.         }    
  209.         /*
  210.          * Draw the days in the week. dayName is a vector of Pascal strings hiding
  211.          * inside a Pascal string.
  212.          */
  213.         dayNamePtr = (unsigned char *) &dayName[1];
  214.         hPos = monthRect.left + 2;
  215.         TextFace(bold);                                        /* 1.0d3                */
  216.         for (weekday = 0; weekday < 7; weekday++) {
  217.             dayWidth = StringWidth((StringPtr) dayNamePtr);
  218.             MoveTo(hPos + dateWidth - dayWidth, vPos);
  219.             DrawString((StringPtr) dayNamePtr);
  220.             dayNamePtr += dayNamePtr[0] + 1;
  221.             hPos += dateWidth;
  222.         }
  223.         TextFace(normal);                                    /* 1.0d3                */
  224.         vPos += lineHeight;
  225.         /*
  226.          * How far do we go in this month, with a leap year hack.
  227.          */
  228.         daysInMonth = kDayInMonth[month];
  229.         if ((year & 0x3) == 0 && month == kFebruary)
  230.             ++daysInMonth;
  231.         /*
  232.          * now.dayOfWeek is the weekday corresponding to the first day of the month.
  233.          * For example, if the first day of the month is on a Sunday, now.dayOfWeek
  234.          * will equal one. firstDayOfWeek will equal one for Sunday, two for Monday.
  235.          */
  236.         weekday = now.dayOfWeek - firstDayOfWeek;
  237.         if (weekday < 0)
  238.             weekday = 6;
  239.         hPos = monthRect.left + 2 + (weekday * dateWidth);
  240.         for (today = 1; today <= daysInMonth; today++, weekday++) {
  241.             if (weekday >= 7) {        /* Wrap around to a new week.                    */
  242.                 hPos = monthRect.left + 2;
  243.                 vPos += lineHeight;
  244.                 weekday = 0;
  245.             }
  246.             /*
  247.              * hOffset locates the left edge of the date -- this will cover one
  248.              * digit for dates 1 to 9, and two digits for 10 to 31.
  249.              */
  250.             hOffset = hPos + spaceWidth;
  251.             if (today < 10) {
  252.                 hOffset += digitWidth;        /* Space over the leftmost digit space    */
  253.                 MoveTo(hOffset, vPos);
  254.                 DrawChar(today + '0');
  255.             }
  256.             else {
  257.                 MoveTo(hOffset, vPos);
  258.                 DrawChar((today / 10) + '0');
  259.                 DrawChar((today % 10) + '0');
  260.             }
  261.             if (isThisMonth && today == thisDate) {            /* 1.0d3                */
  262.                 /*
  263.                  * We are drawing an oval around this date. There is quite a bit of
  264.                  * eyeball adjustment that could be re-adjusted by someone with
  265.                  * more (or less) visual taste.
  266.                  */
  267.                 GetPenState(&penState);
  268.                 penSize = (fontInfo.ascent >= 12) ? fontInfo.ascent / 6 : 1;
  269.                 vOffset = vPos - fontInfo.ascent - penSize;
  270.                 PenSize(penSize, penSize);
  271.                 SetRect(
  272.                     &dayRect,
  273.                     hOffset - penSize,
  274.                     vOffset,
  275.                     hPos + dateWidth + penSize,
  276.                     vOffset + lineHeight + penSize
  277.                 );
  278.                 FrameRoundRect(
  279.                     &dayRect,
  280.                     (dateWidth * 3) / 4,
  281.                     (lineHeight * 3) / 4
  282.                 );
  283.                 SetPenState(&penState);
  284.             }
  285.             hPos += dateWidth;
  286.         }
  287. }
  288.